use std::{rc::Rc, cell::RefCell, sync::Arc};

use crate::{instructions::base::{Instruction, BytecodeReader}, rtda::Frame};

// 给实例变量赋值，它需要三个操作数。前两个操作数是常量池索引和变量值，用法和putstatic一样。第三个操作数是对象引用，从操作数栈中弹出。

#[derive(Debug, Default)]
pub struct PUT_FIELD {
    index: usize
}

impl Instruction for PUT_FIELD {
    fn fetch_operands(&mut self, reader: &mut BytecodeReader) {
        self.index = reader.read_u16() as usize;
    }

    // 给实例变量赋值
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let current_method = frame.borrow().get_method();
        let current_class = current_method.get_class();
        let cp = current_class.get_constant_pool().unwrap();
        let field_ref = cp.get_constant(self.index).unwrap().field_ref();
        let field = field_ref.lock().unwrap().resolved_field();
        
        let class = field.get_class();

        // 解析后的字段必须是实例字段
        if field.is_static() {
            panic!("java.lang.IncompatibleClassChangeError")
        }
        // final 字段为静态常量，只能在构造函数中初始化
        if field.is_final() {
            if !Arc::ptr_eq(&current_class, &class) || current_method.get_name() != "<init>" {
                panic!("java.lang.IllegalAccessError")
            }
        }
        let descriptor = field.get_descriptor();
        let slot_id = field.get_slot_id();
        let stack = frame.borrow().get_operand_stack();
        // 从操作数栈中弹出相应的值，然后赋给静态变量。
        match &descriptor[..1] {
            "Z" | "B"  | "C" | "S" | "I" => {
                let val = stack.borrow_mut().pop_int();
                let _ref = stack.borrow_mut().pop_ref();
                if _ref.is_none() {
                    panic!("java.lang.NullPointerException")
                }
                unsafe { &mut *_ref.unwrap() }.get_fields_mut().set_int(slot_id, val);
            },
            "F" => {
                let val = stack.borrow_mut().pop_float();
                let _ref = stack.borrow_mut().pop_ref();
                if _ref.is_none() {
                    panic!("java.lang.NullPointerException")
                }
                unsafe { &mut *_ref.unwrap() }.get_fields_mut().set_float(slot_id, val);
            },
            "J" => {
                let val = stack.borrow_mut().pop_long();
                let _ref = stack.borrow_mut().pop_ref();
                if _ref.is_none() {
                    panic!("java.lang.NullPointerException")
                }
                unsafe { &mut *_ref.unwrap() }.get_fields_mut().set_long(slot_id, val);
            },
            "D" => {
                let val = stack.borrow_mut().pop_double();
                let _ref = stack.borrow_mut().pop_ref();
                if _ref.is_none() {
                    panic!("java.lang.NullPointerException")
                }
                unsafe { &mut *_ref.unwrap() }.get_fields_mut().set_double(slot_id, val);
            },
            "L" | "[" => {
                let val = stack.borrow_mut().pop_ref();
                let _ref = stack.borrow_mut().pop_ref();
                if _ref.is_none() {
                    panic!("java.lang.NullPointerException")
                }
                unsafe { &mut *_ref.unwrap() }.get_fields_mut().set_ref(slot_id, val);
            },
            _ => ()
            // }
        }
    }


}
